{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Function Introspection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fact(n: \"some non-negative integer\") -> \"n! or 0 if n < 0\":\n",
    "    \"\"\"Calculates the factorial of a non-negative integer n\n",
    "    \n",
    "    If n is negative, returns 0.\n",
    "    \"\"\"\n",
    "    if n < 0:\n",
    "        return 0\n",
    "    elif n <= 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return n * fact(n-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since functions are objects, we can add attributes to a function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "fact.short_description = \"factorial function\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "factorial function\n"
     ]
    }
   ],
   "source": [
    "print(fact.short_description)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see all the attributes that belong to a function using the **dir** function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__annotations__',\n",
       " '__call__',\n",
       " '__class__',\n",
       " '__closure__',\n",
       " '__code__',\n",
       " '__defaults__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__get__',\n",
       " '__getattribute__',\n",
       " '__globals__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__kwdefaults__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__name__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__qualname__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " 'short_description']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(fact)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see our **short_description** attribute, as well as some attributes we have seen before: **__annotations__** and **__doc__**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Calculates the factorial of a non-negative integer n\\n    \\n    If n is negative, returns 0.\\n    '"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fact.__doc__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'n': 'some non-negative integer', 'return': 'n! or 0 if n < 0'}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fact.__annotations__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll revisit some of these attributes later in this course, but let's take a look at a few here:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a, b=2, c=3, *, kw1, kw2=2, **kwargs):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's assign my_func to another variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f = my_func"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The **__name__** attribute holds the function's name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'my_func'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__name__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'my_func'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f.__name__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The **__defaults__** attribute is a tuple containing any positional parameter defaults:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__defaults__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'kw2': 2}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__kwdefaults__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's create a function with some local variables:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(a, b=1, *args, **kwargs):\n",
    "    i = 10\n",
    "    b = min(i, b)\n",
    "    return a * b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'aaaaaaaaaa'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func('a', 100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The **__code__** attribute contains a **code** object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<code object my_func at 0x0000016640E71300, file \"<ipython-input-13-785cf1a800f4>\", line 1>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__code__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This **code** object itself has various properties:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " 'co_argcount',\n",
       " 'co_cellvars',\n",
       " 'co_code',\n",
       " 'co_consts',\n",
       " 'co_filename',\n",
       " 'co_firstlineno',\n",
       " 'co_flags',\n",
       " 'co_freevars',\n",
       " 'co_kwonlyargcount',\n",
       " 'co_lnotab',\n",
       " 'co_name',\n",
       " 'co_names',\n",
       " 'co_nlocals',\n",
       " 'co_stacksize',\n",
       " 'co_varnames']"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(my_func.__code__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Attribute **__co_varnames__** is a tuple containing the parameter names and local variables:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('a', 'b', 'args', 'kwargs', 'i')"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__code__.co_varnames"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Attribute **co_argcount** returns the number of arguments (minus any \\* and \\*\\* args)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_func.__code__.co_argcount"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### The **inspect** module"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is much easier to use the **inspect** module!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import inspect"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(my_func)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By the way, there is a difference between a function and a method! A method is a function that is bound to some object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.ismethod(my_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class MyClass:\n",
    "    def f_instance(self):\n",
    "        pass\n",
    "    \n",
    "    @classmethod\n",
    "    def f_class(cls):\n",
    "        pass\n",
    "    \n",
    "    @staticmethod\n",
    "    def f_static():\n",
    "        pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Instance methods** are bound to the **instance** of a class (not the class itself)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Class methods** are bound to the **class**, not instances"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Static methods** are no bound either to the class or its instances"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, False)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(MyClass.f_instance), inspect.ismethod(MyClass.f_instance)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(False, True)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(MyClass.f_class), inspect.ismethod(MyClass.f_class)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, False)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(MyClass.f_static), inspect.ismethod(MyClass.f_static)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "my_obj = MyClass()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(False, True)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(my_obj.f_instance), inspect.ismethod(my_obj.f_instance)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(False, True)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(my_obj.f_class), inspect.ismethod(my_obj.f_class)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, False)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isfunction(my_obj.f_static), inspect.ismethod(my_obj.f_static)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you just want to know if something is a function or method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isroutine(my_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isroutine(MyClass.f_instance)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isroutine(my_obj.f_class)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.isroutine(my_obj.f_static)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll revisit this in more detail in section on OOP."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Introspecting Callable Code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can get back the source code of our function using the **getsource()** method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'def fact(n: \"some non-negative integer\") -> \"n! or 0 if n < 0\":\\n    \"\"\"Calculates the factorial of a non-negative integer n\\n    \\n    If n is negative, returns 0.\\n    \"\"\"\\n    if n <= 1:\\n        return 1\\n    else:\\n        return n * fact(n-1)\\n'"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getsource(fact)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "def fact(n: \"some non-negative integer\") -> \"n! or 0 if n < 0\":\n",
      "    \"\"\"Calculates the factorial of a non-negative integer n\n",
      "    \n",
      "    If n is negative, returns 0.\n",
      "    \"\"\"\n",
      "    if n <= 1:\n",
      "        return 1\n",
      "    else:\n",
      "        return n * fact(n-1)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getsource(fact))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'    def f_instance(self):\\n        pass\\n'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getsource(MyClass.f_instance)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'    def f_instance(self):\\n        pass\\n'"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getsource(my_obj.f_instance)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also find out where the function was defined:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module '__main__'>"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getmodule(fact)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'builtins' (built-in)>"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getmodule(print)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import math"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'math' (built-in)>"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getmodule(math.sin)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# setting up variable\n",
    "i = 10\n",
    "\n",
    "# comment line 1\n",
    "# comment line 2\n",
    "def my_func(a, b=1):\n",
    "    # comment inside my_func\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'# comment line 1\\n# comment line 2\\n'"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.getcomments(my_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# comment line 1\n",
      "# comment line 2\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getcomments(my_func))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Introspecting Callable Signatures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO: Provide implementation\n",
    "def my_func(a: 'a string', \n",
    "            b: int = 1, \n",
    "            *args: 'additional positional args', \n",
    "            kw1: 'first keyword-only arg', \n",
    "            kw2: 'second keyword-only arg' = 10,\n",
    "            **kwargs: 'additional keyword-only args') -> str:\n",
    "    \"\"\"does something\n",
    "       or other\"\"\"\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Signature (a:'a string', b:int=1, *args:'additional positional args', kw1:'first keyword-only arg', kw2:'second keyword-only arg'=10, **kwargs:'additional keyword-only args') -> str>"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inspect.signature(my_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "inspect.Signature"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(inspect.signature(my_func))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "sig = inspect.signature(my_func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__class__',\n",
       " '__delattr__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__setstate__',\n",
       " '__sizeof__',\n",
       " '__slots__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '_bind',\n",
       " '_bound_arguments_cls',\n",
       " '_hash_basis',\n",
       " '_parameter_cls',\n",
       " '_parameters',\n",
       " '_return_annotation',\n",
       " 'bind',\n",
       " 'bind_partial',\n",
       " 'empty',\n",
       " 'from_builtin',\n",
       " 'from_callable',\n",
       " 'from_function',\n",
       " 'parameters',\n",
       " 'replace',\n",
       " 'return_annotation']"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(sig)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a a:'a string'\n",
      "b b:int=1\n",
      "args *args:'additional positional args'\n",
      "kw1 kw1:'first keyword-only arg'\n",
      "kw2 kw2:'second keyword-only arg'=10\n",
      "kwargs **kwargs:'additional keyword-only args'\n"
     ]
    }
   ],
   "source": [
    "for param_name, param in sig.parameters.items():\n",
    "    print(param_name, param)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def print_info(f: \"callable\") -> None:\n",
    "    print(f.__name__)\n",
    "    print('=' * len(f.__name__), end='\\n\\n')\n",
    "    \n",
    "    print('{0}\\n{1}\\n'.format(inspect.getcomments(f), \n",
    "                              inspect.cleandoc(f.__doc__)))\n",
    "    \n",
    "    print('{0}\\n{1}'.format('Inputs', '-'*len('Inputs')))\n",
    "    \n",
    "    sig = inspect.signature(f)\n",
    "    for param in sig.parameters.values():\n",
    "        print('Name:', param.name)\n",
    "        print('Default:', param.default)\n",
    "        print('Annotation:', param.annotation)\n",
    "        print('Kind:', param.kind)\n",
    "        print('--------------------------\\n')\n",
    "        \n",
    "    print('{0}\\n{1}'.format('\\n\\nOutput', '-'*len('Output')))\n",
    "    print(sig.return_annotation)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "my_func\n",
      "=======\n",
      "\n",
      "# TODO: Provide implementation\n",
      "\n",
      "does something\n",
      "or other\n",
      "\n",
      "Inputs\n",
      "------\n",
      "Name: a\n",
      "Default: <class 'inspect._empty'>\n",
      "Annotation: a string\n",
      "Kind: POSITIONAL_OR_KEYWORD\n",
      "--------------------------\n",
      "\n",
      "Name: b\n",
      "Default: 1\n",
      "Annotation: <class 'int'>\n",
      "Kind: POSITIONAL_OR_KEYWORD\n",
      "--------------------------\n",
      "\n",
      "Name: args\n",
      "Default: <class 'inspect._empty'>\n",
      "Annotation: additional positional args\n",
      "Kind: VAR_POSITIONAL\n",
      "--------------------------\n",
      "\n",
      "Name: kw1\n",
      "Default: <class 'inspect._empty'>\n",
      "Annotation: first keyword-only arg\n",
      "Kind: KEYWORD_ONLY\n",
      "--------------------------\n",
      "\n",
      "Name: kw2\n",
      "Default: 10\n",
      "Annotation: second keyword-only arg\n",
      "Kind: KEYWORD_ONLY\n",
      "--------------------------\n",
      "\n",
      "Name: kwargs\n",
      "Default: <class 'inspect._empty'>\n",
      "Annotation: additional keyword-only args\n",
      "Kind: VAR_KEYWORD\n",
      "--------------------------\n",
      "\n",
      "\n",
      "\n",
      "Output\n",
      "------\n",
      "<class 'str'>\n"
     ]
    }
   ],
   "source": [
    "print_info(my_func)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A Side Note on Positional Only Arguments"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some built-in callables have arguments that are positional only (i.e. cannot be specified using a keyword).\n",
    "\n",
    "However, Python does not currently have any syntax that allows us to define callables with positional only arguments.\n",
    "\n",
    "In general, the documentation uses a **/** character to indicate that all preceding arguments are positional-only. But not always :-("
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function divmod in module builtins:\n",
      "\n",
      "divmod(x, y, /)\n",
      "    Return the tuple (x//y, x%y).  Invariant: div*y + mod == x.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(divmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we see that the **divmod** function takes two positional-only parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3, 1)"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "divmod(10, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "divmod() takes no keyword arguments",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-55-c637b01eef33>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mdivmod\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: divmod() takes no keyword arguments"
     ]
    }
   ],
   "source": [
    "divmod(x=10, y=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, the string **replace** function also takes positional-only arguments, however, the documentation does not indicate this!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on method_descriptor:\n",
      "\n",
      "replace(...)\n",
      "    S.replace(old, new[, count]) -> str\n",
      "    \n",
      "    Return a copy of S with all occurrences of substring\n",
      "    old replaced by new.  If the optional argument count is\n",
      "    given, only the first count occurrences are replaced.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(str.replace)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'xyzdefg'"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'abcdefg'.replace('abc', 'xyz')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "replace() takes no keyword arguments",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-58-9d61ac657cae>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;34m'abcdefg'\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreplace\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mold\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'abc'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mnew\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'xyz'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: replace() takes no keyword arguments"
     ]
    }
   ],
   "source": [
    "'abcdefg'.replace(old='abc', new='xyz')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
